Analysis of Housing Rental Market

Impact of Unemployment and Median Income on Rental Rates

This study is a review of the Rental Market and what, if any impact Unemployment Rates and Median Income have on pricing. The data curated for this study spans from 2013 to 2018. Additional Observations have been provided for further context.

Curated Data Sets

In [81]:
#Describe Dataset here
filename = 'data\zillow\MortgageRateConventionalFixed.csv'
mortgage_rates_src = pd.read_csv(filename)

#Describe Dataset here
filename = 'data\zillow\Affordability_Income_2018Q4.csv'
affordability_income_src = pd.read_csv(filename,encoding='latin-1',skipinitialspace=True)

#Median Rental Price - Single Family Residence
filename = 'data\zillow\City_MedianRentalPrice_Sfr.csv'
median_rental_price_sfr_src = pd.read_csv(filename,skipinitialspace=True)

#Median Rental Price Per Square Foot - Single Family Residence
filename = 'data\zillow\City_MedianRentalPricePerSqft_Sfr.csv'
median_rental_price_sfr_sqft_src = pd.read_csv(filename,skipinitialspace=True)

#Unemployment Rate - 20 years 
filename = 'data/unemployment_rate_20_years.csv'
unemployment_rate_src = pd.read_csv(filename,encoding='latin-1',skipinitialspace=True)

#Monthly Listing all homes - city
filename = 'data/zillow/MonthlyListings_NSA_AllHomes_City.csv'
monthly_listings_allhomes_city_src = pd.read_csv(filename,encoding='latin-1',skipinitialspace=True)

#Sale Counts - city
filename = 'data/zillow/Sale_Counts_City.csv'
monthly_sale_counts_city_src = pd.read_csv(filename,encoding='latin-1',skipinitialspace=True)

#Sale prices - city
filename = 'data/zillow/Sale_Prices_City.csv'
monthly_sale_prices_city_src = pd.read_csv(filename,encoding='latin-1',skipinitialspace=True)

#Describe Dataset here
filename = 'data\zillow\Affordability_Wide_2019Q2_Public.csv'
affordability_index_src = pd.read_csv(filename,encoding='latin-1',skipinitialspace=True)

#Unemployment Rate - by state 
filename = 'data/unemployment_rate_by_state.csv'
unemployment_rate_state_src = pd.read_csv(filename,encoding='latin-1',skipinitialspace=True) 

#    #Median Rental Price - Multi-Family Residence
#    filename = 'data\zillow\City_MedianRentalPrice_Mfr5Plus.csv'
#    median_rental_price_mfr_src = pd.read_csv(filename,skipinitialspace=True)

#    #Median Rental Price Per Square Foot - Multi-Family Residence
#    filename = 'data\zillow\City_MedianRentalPricePerSqft_Mfr5Plus.csv'
#    median_rental_price_mfr_sqft_src = pd.read_csv(filename,skipinitialspace=True)

#    #Rental Index Per Square Foot - All Homes
#    filename = 'data\zillow\City_ZriPerSqft_AllHomes.csv'
#    rental_index_all_sqft_src = pd.read_csv(filename,encoding='latin-1',skipinitialspace=True)

#    #Median Value - All Homes
#    filename = 'data\zillow\City_Zhvi_AllHomes.csv'
#    median_value_all_src = pd.read_csv(filename,encoding='latin-1',skipinitialspace=True)

#    #Median Value Per Square Foot - All Homes
#    filename = 'data\zillow\City_MedianValuePerSqft_AllHomes.csv'
#    median_value_all_sqft_src = pd.read_csv(filename,encoding='latin-1',skipinitialspace=True)

#    #Count of Home Sales
#    filename = 'data\zillow\Sale_Counts_City.csv'
#    sales_count_src = pd.read_csv(filename,encoding='latin-1',skipinitialspace=True)

#    #rental vacancy Rate - 10 years quarterly
#    filename = 'data/rental_vacancy_rate_10_yrs.csv'
#    rental_vacancy_rate_src = pd.read_csv(filename,encoding='latin-1',skipinitialspace=True)

#Variable for use in the notebook
housing_crash_date = ['2008','12']
state_geo = 'data/folium/us-states.json'

Annual Metrics Munging

Median Rental Rate - sqft - Monthly

In [82]:
#hideme
median_rental_price_sfr_sqft = median_rental_price_sfr_sqft_src.copy()
median_rental_price_sfr_sqft.drop(columns=['State','RegionName','Metro','CountyName','SizeRank'],inplace=True)
median_rental_price_sfr_sqft.columns = pd.MultiIndex.from_tuples([tuple(c.split('-'))for c in median_rental_price_sfr_sqft.columns],names=['Year','Month'])
median_rental_price_sfr_sqft = pd.DataFrame(median_rental_price_sfr_sqft.median()).reset_index()
median_rental_price_sfr_sqft.columns=['Year','Month','RentalRateSqFt']
median_rental_price_sfr_sqft['Year'] = median_rental_price_sfr_sqft['Year'].astype(int)
median_rental_price_sfr_sqft['Month'] = median_rental_price_sfr_sqft['Month'].astype(int)
median_rental_price_sfr_sqft.head()
Out[82]:
Year Month RentalRateSqFt
0 2010 1 0.670390
1 2010 2 0.776057
2 2010 3 0.786121
3 2010 4 0.784661
4 2010 5 0.785475

Annual Metrics Munging

Median Rental Rate - Monthly

In [83]:
median_rental_price_sfr = median_rental_price_sfr_src.copy()
median_rental_price_sfr.drop(columns=['State','RegionName','Metro','CountyName','SizeRank'],inplace=True)
median_rental_price_sfr.columns = pd.MultiIndex.from_tuples([tuple(c.split('-'))for c in median_rental_price_sfr.columns],names=['Year','Month'])
median_rental_price_sfr = pd.DataFrame(median_rental_price_sfr.median()).reset_index()
median_rental_price_sfr.columns=['Year','Month','RentalRate']
median_rental_price_sfr['Year'] = median_rental_price_sfr['Year'].astype(int)
median_rental_price_sfr['Month'] = median_rental_price_sfr['Month'].astype(int)
#median_rental_price_sfr.set_index('Year',inplace=True)
median_rental_price_sfr.head()
Out[83]:
Year Month RentalRate
0 2010 1 925.00
1 2010 2 1300.00
2 2010 3 1236.25
3 2010 4 1250.00
4 2010 5 1250.00

Annual Metrics Munging

Mortgage Rates - Monthly

In [84]:
mortgage_rates = mortgage_rates_src.copy()
mortgage_rates = mortgage_rates.drop(columns='TimePeriod')
mortgage_rates = mortgage_rates.rename(columns={'MortgageRateConventionalFixed':'MortgageRate'})
mortgage_rates['Date'] = pd.to_datetime(mortgage_rates['Date'], format='%Y-%m-%d')
mortgage_rates['Year'] = pd.DatetimeIndex(mortgage_rates['Date']).year
mortgage_rates['Month'] = pd.DatetimeIndex(mortgage_rates['Date']).month
mortgage_rates = mortgage_rates.drop(columns='Date')
mortgage_rates = mortgage_rates.groupby(['Year','Month']).median().reset_index()
mortgage_rates['Year'] = mortgage_rates['Year'].astype(int)
mortgage_rates['Month'] = mortgage_rates['Month'].astype(int)
#mortgage_rates.set_index('Year',inplace=True)
mortgage_rates.head()
Out[84]:
Year Month MortgageRate
0 2011 6 4.36
1 2011 7 4.45
2 2011 8 4.12
3 2011 9 3.97
4 2011 10 4.06

Annual Metrics Munging

Unemployment Rates - Monthly

In [85]:
unemployment_rate = pd.DataFrame(unemployment_rate_src.copy())#.reset_index()
unemployment_rate = unemployment_rate.rename(columns={'LNS14000024':'UnemploymentRate'})
unemployment_rate['Date'] = pd.to_datetime(unemployment_rate['DATE'], format='%Y-%m-%d')
unemployment_rate['Year'] = pd.DatetimeIndex(unemployment_rate['Date']).year
unemployment_rate['Month'] = pd.DatetimeIndex(unemployment_rate['Date']).month
unemployment_rate = unemployment_rate.drop(columns='Date')
unemployment_rate = unemployment_rate.groupby(['Year','Month']).median().reset_index()
unemployment_rate['Year'] = unemployment_rate['Year'].astype(int)
unemployment_rate['Month'] = unemployment_rate['Month'].astype(int)
unemployment_rate.head()
Out[85]:
Year Month UnemploymentRate
0 2009 8 8.9
1 2009 9 9.1
2 2009 10 9.3
3 2009 11 9.2
4 2009 12 9.2

Annual Metrics Munging

Median Income - Monthly

In [86]:
affordability_income = affordability_income_src.copy()
affordability_income['State'] = affordability_income["RegionName"].apply(lambda x: "".join(x.split(',')[1:]))
#affordability_income['City'] = affordability_income["RegionName"].apply(lambda x: "".join(x.split(',')[0]))
affordability_income.drop(columns=['RegionName','RegionID','SizeRank'],inplace=True)
affordability_income = affordability_income.set_index('State')
affordability_income.columns = pd.MultiIndex.from_tuples([tuple(c.split('-'))for c in affordability_income.columns],names=['Year','Month'])
#affordability_income = affordability_income.groupby('State').sum()#.median()
affordability_income = pd.DataFrame(affordability_income.median()).reset_index()
affordability_income.columns=['Year','Month','MedianIncome']
affordability_income['Year'] = affordability_income['Year'].astype(int)
affordability_income['Month'] = affordability_income['Month'].astype(int)
##median_rental_price_sfr.set_index('Year',inplace=True)
affordability_income.head()
Out[86]:
Year Month MedianIncome
0 1979 3 15575.635
1 1979 6 15916.245
2 1979 9 16213.680
3 1979 12 16502.270
4 1980 3 16842.490

Annual Metrics Munging

Home Sales - Monthly

this dataset includes small cities so we're going to total by state before doing median

In [87]:
monthly_sale_counts_city = monthly_sale_counts_city_src.copy()
monthly_sale_counts_city.drop(columns=['RegionName','RegionID','SizeRank'],inplace=True)
monthly_sale_counts_city = monthly_sale_counts_city.set_index('StateName')
monthly_sale_counts_city.columns = pd.MultiIndex.from_tuples([tuple(c.split('-'))for c in monthly_sale_counts_city.columns],names=['Year','Month'])
#this dataset includes small cities so we're going to total by state before doing median
monthly_sale_counts_city = monthly_sale_counts_city.groupby('StateName').sum()#.median()
monthly_sale_counts_city = pd.DataFrame(monthly_sale_counts_city.median()).reset_index()
monthly_sale_counts_city.columns=['Year','Month','SaleCounts']
monthly_sale_counts_city['Year'] = monthly_sale_counts_city['Year'].astype(int)
monthly_sale_counts_city['Month'] = monthly_sale_counts_city['Month'].astype(int)
##median_rental_price_sfr.set_index('Year',inplace=True)
monthly_sale_counts_city.head()
Out[87]:
Year Month SaleCounts
0 2008 3 1939.0
1 2008 4 2168.0
2 2008 5 2365.0
3 2008 6 2694.0
4 2008 7 2726.0

Annual Metrics Munging

Home Sales Prices - Monthly

In [88]:
monthly_sale_prices_city = monthly_sale_prices_city_src.copy()
monthly_sale_prices_city.drop(columns=['StateName','RegionName','RegionID','SizeRank'],inplace=True)
monthly_sale_prices_city.columns = pd.MultiIndex.from_tuples([tuple(c.split('-'))for c in monthly_sale_prices_city.columns],names=['Year','Month'])
monthly_sale_prices_city = pd.DataFrame(monthly_sale_prices_city.median()).reset_index()
monthly_sale_prices_city.columns=['Year','Month','SalePrices']
monthly_sale_prices_city['Year'] = monthly_sale_prices_city['Year'].astype(int)
monthly_sale_prices_city['Month'] = monthly_sale_prices_city['Month'].astype(int)
#median_rental_price_sfr.set_index('Year',inplace=True)
monthly_sale_prices_city.head()
Out[88]:
Year Month SalePrices
0 2008 3 246400.0
1 2008 4 242600.0
2 2008 5 236600.0
3 2008 6 233900.0
4 2008 7 230900.0

Annual Metrics Munging

Home Listings - Monthly

this dataset includes small cities so we're going to total by state before doing median

In [89]:
monthly_listings_allhomes_city = monthly_listings_allhomes_city_src.copy()
monthly_listings_allhomes_city.drop(columns=['RegionName','RegionID','SizeRank'],inplace=True)
monthly_listings_allhomes_city = monthly_listings_allhomes_city.set_index('StateName')
monthly_listings_allhomes_city.columns = pd.MultiIndex.from_tuples([tuple(c.split('-'))for c in monthly_listings_allhomes_city.columns],names=['Year','Month'])
monthly_listings_allhomes_city = monthly_listings_allhomes_city.groupby('StateName').sum()
monthly_listings_allhomes_city = pd.DataFrame(monthly_listings_allhomes_city.median()).reset_index()
monthly_listings_allhomes_city.columns=['Year','Month','Listings']
monthly_listings_allhomes_city['Year'] = monthly_listings_allhomes_city['Year'].astype(int)
monthly_listings_allhomes_city['Month'] = monthly_listings_allhomes_city['Month'].astype(int)
#median_rental_price_sfr.set_index('Year',inplace=True)
monthly_listings_allhomes_city.head()
Out[89]:
Year Month Listings
0 2013 1 18950.5
1 2013 2 18459.0
2 2013 3 19230.5
3 2013 4 20254.0
4 2013 5 21811.0

Annual Metrics Munging

Consolidated Annual Metrics

*MedianIncome is a quarterly figure. A fillna backfill method was chosen to address NaN values and a left merge was used to respect the consolidated range of availble dates of the set.

In [90]:
#build metrics set from first candidate df
metrics_yearly = median_rental_price_sfr_sqft
metrics_yearly['Date'] = metrics_yearly['Month'].map(str)+ '-' +metrics_yearly['Year'].map(str)
metrics_yearly['Date'] = pd.to_datetime(metrics_yearly['Date'], format='%m-%Y')#.dt.strftime('%Y-Q%q')
metrics_yearly['Quarter'] = metrics_yearly['Date'].dt.quarter
#set our column order early so we don't have to deal with it later
metrics_yearly = metrics_yearly[['Date','Year','Month','Quarter','RentalRateSqFt']]
#merge in any other metrics we need
metrics_yearly = metrics_yearly.merge(unemployment_rate, left_on=['Year','Month'], right_on=['Year','Month'])
metrics_yearly = metrics_yearly.merge(median_rental_price_sfr, left_on=['Year','Month'], right_on=['Year','Month'])
metrics_yearly = metrics_yearly.merge(mortgage_rates,left_on=['Year','Month'], right_on=['Year','Month'])
metrics_yearly = metrics_yearly.merge(monthly_listings_allhomes_city,left_on=['Year','Month'], right_on=['Year','Month'])
metrics_yearly = metrics_yearly.merge(monthly_sale_counts_city,left_on=['Year','Month'], right_on=['Year','Month'])
metrics_yearly = metrics_yearly.merge(monthly_sale_prices_city,left_on=['Year','Month'], right_on=['Year','Month'])
metrics_yearly = metrics_yearly.merge(affordability_income,how='left',left_on=['Year','Month'], right_on=['Year','Month']).fillna(method='bfill')
metrics_yearly = metrics_yearly.dropna(how='any')
metrics_yearly.set_index('Date',inplace=True,drop=True)
#metrics_yearly['Quarter'] = metrics_yearly['Date']
#metrics_yearly = metrics_yearly.resample('Q').mean()
metrics_yearly.head()
Out[90]:
Year Month Quarter RentalRateSqFt UnemploymentRate RentalRate MortgageRate Listings SaleCounts SalePrices MedianIncome
Date
2013-01-01 2013 1 1 0.817461 7.4 1250.0 3.46 18950.5 2118.0 189500.0 48236.435
2013-02-01 2013 2 1 0.821955 7.0 1250.0 3.58 18459.0 1870.0 188400.0 48236.435
2013-03-01 2013 3 1 0.831094 6.9 1288.0 3.59 19230.5 2348.0 189300.0 48236.435
2013-04-01 2013 4 2 0.831767 6.9 1295.0 3.42 20254.0 2951.0 191500.0 48455.295
2013-05-01 2013 5 2 0.830218 6.8 1300.0 3.59 21811.0 3331.0 192150.0 48455.295
In [91]:
#hideme
metrics_yearly.describe()
Out[91]:
Year Month Quarter RentalRateSqFt UnemploymentRate RentalRate MortgageRate Listings SaleCounts SalePrices MedianIncome
count 72.00000 72.000000 72.00000 72.000000 72.000000 72.000000 72.000000 72.000000 72.000000 72.000000 72.000000
mean 2015.50000 6.500000 2.50000 0.910004 4.877778 1465.475694 3.988194 19086.125000 3846.916667 217338.888889 51727.052195
std 1.71981 3.476278 1.12588 0.068399 1.102522 110.281803 0.353184 2543.764653 917.341440 16210.267183 2531.378580
min 2013.00000 1.000000 1.00000 0.817461 3.400000 1250.000000 3.410000 13548.500000 1870.000000 188400.000000 48236.435000
25% 2014.00000 3.750000 1.75000 0.852494 4.000000 1395.000000 3.747500 17494.750000 3294.250000 203650.000000 49470.261250
50% 2015.50000 6.500000 2.50000 0.895257 4.600000 1456.000000 3.920000 19019.250000 3791.500000 216075.000000 51609.077500
75% 2017.00000 9.250000 3.25000 0.978971 5.625000 1582.187500 4.245000 20829.375000 4571.750000 228350.000000 54045.258750
max 2018.00000 12.000000 4.00000 1.033592 7.400000 1650.000000 4.850000 23971.500000 5933.000000 248900.000000 55962.065107

Analysis of Consolidated Metrics

Rental Rates vs Unemployment by Year

Rental Rates vs Unemployment by Year

Analysis of Consolidated Metrics

Rental Rates vs Median Income

Rental Rates vs Median Income by Year

Analysis of Consolidated Metrics

Rental Rates vs All Observations by Year

Rental Rates vs All Observations by Year

Analysis of Consolidated Metrics

Scatter Matrix of All Observations

Scatter Matrix of All Observations

Analysis of Consolidated Metrics

Scatter Matrix of Core Observations

In [96]:
metrics_yearly_core =  metrics_yearly.filter(items=['Year','Month','Quarter','RentalRateSqFt','UnemploymentRate','RentalRate','MedianIncome'])
metrics_yearly_core.head()
Out[96]:
Year Month Quarter RentalRateSqFt UnemploymentRate RentalRate MedianIncome
Date
2013-01-01 2013 1 1 0.817461 7.4 1250.0 48236.435
2013-02-01 2013 2 1 0.821955 7.0 1250.0 48236.435
2013-03-01 2013 3 1 0.831094 6.9 1288.0 48236.435
2013-04-01 2013 4 2 0.831767 6.9 1295.0 48455.295
2013-05-01 2013 5 2 0.830218 6.8 1300.0 48455.295
In [97]:
#hideme
title = 'Scatter Matrix of Core Observations'


df = metrics_yearly_core.copy()#.drop(columns=['Month','Quarter'])

sns.pairplot(df, hue="Year", vars=['RentalRateSqFt','RentalRate','UnemploymentRate','MedianIncome'])

# Save the Figure
plt.savefig(f"figures\{title.replace(' ','_')}.png")

# Show the Figure
plt.show()

Scatter Matrix of Core Observations

Analysis of Consolidated Metrics

RentalRateSqFt vs UnemploymentRate - Regression

RentalRateSqFt vs UnemploymentRate

Analysis of Consolidated Metrics

RentalRateSqFt vs MedianIncome - Regression

RentalRateSqFt vs MedianIncome

Analysis of Consolidated Metrics

Unemployment Rates by Year

Unemployment Rates by Year

Analysis of Consolidated Metrics

MedianIncome by Year

MedianIncome by Year

ANOVA testing of Hypothesis

Null Hypothysis "Unemployment Rate has no correlation to Rental Rates"

ANOVA Boxplot - RentalRateSqFt vs Unemployment

In [103]:
groups = metrics_yearly_anova_emp['UnemploymentRate'].unique()
groups
Out[103]:
array([7.4, 7. , 6.9, 6.8, 6.7, 6.6, 6.4, 6.2, 6.1, 5.8, 5.5, 5.6, 5.7,
       5.4, 5.2, 5.1, 5. , 4.8, 4.6, 4.7, 4.5, 4.4, 4.3, 4.1, 4. , 3.9,
       3.7, 3.6, 3.5, 3.4])
In [104]:
stats.f_oneway(*[metrics_yearly_anova_emp[metrics_yearly_anova_emp["UnemploymentRate"] == grp]["RentalRateSqFt"] for grp in groups])
Out[104]:
F_onewayResult(statistic=79.66967201737702, pvalue=2.2620372064918147e-28)

ANOVA testing of Hypothesis

Null Hypothysis "Median Income has no correlation to Rental Rates"

ANOVA Boxplot - RentalRateSqFt vs MedianIncome

In [106]:
groups = metrics_yearly_anova_inc['MedianIncome'].unique()
groups
Out[106]:
array([48236.44, 48455.3 , 48314.18, 48463.3 , 48549.18, 49094.68,
       49595.46, 50160.4 , 50425.57, 50603.34, 50866.74, 51414.72,
       51803.44, 52228.64, 52590.16, 53035.24, 53376.15, 53934.84,
       54376.53, 54400.16, 54793.87, 55187.58, 55581.29, 55962.07])
In [107]:
stats.f_oneway(*[metrics_yearly_anova_inc[metrics_yearly_anova_inc["MedianIncome"] == grp]["RentalRateSqFt"] for grp in groups])
Out[107]:
F_onewayResult(statistic=384.5884229347032, pvalue=8.331698465058281e-47)

Additional Observations

Affordability Index - Monthly

pti - Price To Income Index
mga - Mortgage Affordability Index
rta - Rent Affordability Index

Affordability Index by Year

Additional Observations

Geodata Munging

In [109]:
#build state name / id xref
with open(state_geo) as json_file:
    state_geo_df = json.load(json_file)
state_geo_xref = json_normalize(state_geo_df['features']).filter(items=['id','properties.name'])
state_geo_xref = state_geo_xref.rename(columns={'id':'State','properties.name':'StateName'})#.set_index('State')
state_geo_xref.head()
Out[109]:
State StateName
0 AL Alabama
1 AK Alaska
2 AZ Arizona
3 AR Arkansas
4 CA California
In [110]:
# create geodataframe and add centroids
gdf = gpd.GeoDataFrame.from_features(state_geo_df)
gdf['centroid_lon'] = gdf['geometry'].centroid.x
gdf['centroid_lat'] = gdf['geometry'].centroid.y
gdf.crs = {'init' :'epsg:4326'}
gdf.head()
Out[110]:
geometry name centroid_lon centroid_lat
0 POLYGON ((-87.359296 35.00118, -85.606675 34.9... Alabama -86.827783 32.789907
1 (POLYGON ((-131.602021 55.117982, -131.569159 ... Alaska -152.373738 64.227768
2 POLYGON ((-109.042503 37.000263, -109.04798 31... Arizona -111.663296 34.293393
3 POLYGON ((-94.473842 36.501861, -90.152536 36.... Arkansas -92.440920 34.898249
4 POLYGON ((-123.233256 42.006186, -122.378853 4... California -119.614389 37.253895
In [111]:
#merge w/ data points
median_rental_price_mfr_state_year = median_rental_price_sfr_src.copy()
median_rental_price_mfr_state_year = median_rental_price_mfr_state_year.merge(state_geo_xref, left_on='State', right_on='State')
median_rental_price_mfr_state_year.set_index(['RegionName','State','Metro','CountyName','SizeRank'],inplace=True)
median_rental_price_mfr_state_year.columns = pd.MultiIndex.from_tuples([tuple(c.split('-'))for c in median_rental_price_mfr_state_year.columns],names=['Year','Month'])
median_rental_price_mfr_state_year = median_rental_price_mfr_state_year.stack().reset_index().groupby(['State','StateName']).median().drop(columns='SizeRank')
gdf_income_tax = gdf.merge(median_rental_price_mfr_state_year.reset_index(),how='right', left_on='name', right_on='StateName')
gdf_income_tax = gdf_income_tax.fillna(method='bfill',axis=1).dropna(how='all',axis=1)
gdf_income_tax.head()
Out[111]:
geometry name centroid_lon centroid_lat State StateName 2010 2011 2012 2013 2014 2015 2016 2017 2018
0 POLYGON ((-87.359296 35.00118, -85.606675 34.9... Alabama -86.8278 32.7899 AL Alabama 980 1075 950 900 850 935 905 950 1118.75
1 (POLYGON ((-131.602021 55.117982, -131.569159 ... Alaska -152.374 64.2278 AK Alaska 1882.5 1882.5 1900 1950 1975 2095 1890 1600 1750
2 POLYGON ((-109.042503 37.000263, -109.04798 31... Arizona -111.663 34.2934 AZ Arizona 1060 1027.25 1068.75 1087.5 1143.75 1195 1173 1350 1497.5
3 POLYGON ((-94.473842 36.501861, -90.152536 36.... Arkansas -92.4409 34.8982 AR Arkansas 928.75 928.75 1150 1100 1150 1125 995 1075 1050
4 POLYGON ((-123.233256 42.006186, -122.378853 4... California -119.614 37.2539 CA California 2825 1821.25 1866.25 1998.75 2173.75 2300 2397.5 2625 2800

MAP - Unemployment Rate (%)

In [112]:
#hideme
unemployment_rate_state = unemployment_rate_state_src.copy()
unemployment_rate_state.rename(columns={'State':'StateName'},inplace=True)
unemployment_rate_state = unemployment_rate_state.merge(state_geo_xref,left_on='StateName',right_on='StateName')
unemployment_rate_state.dropna(inplace=True)
years = [col for col in unemployment_rate_state.columns if col not in ['StateName','State']]


#render the map
map_name = 'Unemployment Rate (%)'
m = folium.Map([43, -100], zoom_start=4)

for year in years:
    yr = folium.Choropleth(
    geo_data=state_geo,
    data=unemployment_rate_state,
    columns=['State', year],
    key_on='feature.id',
    fill_color='YlGn',
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name= f'{year} - {map_name}',
    overlay=True,
    name = f'{year}'
    )
    m.add_child(yr)

m.add_child(folium.LayerControl())

m.save(f"maps\{map_name}.html")

m
Out[112]:

MAP - Median Income ($)

In [113]:
#hideme
affordability_income_map_median = affordability_income_src.copy()
affordability_income_map_median['State'] = affordability_income_map_median["RegionName"].apply(lambda x: "".join(x.split(',')[1:]))
affordability_income_map_median.drop(columns=['RegionName','RegionID','SizeRank'],inplace=True)
affordability_income_map_median = affordability_income_map_median.set_index('State')
affordability_income_map_median.columns = pd.MultiIndex.from_tuples([tuple(c.split('-'))for c in affordability_income_map_median.columns],names=['Year','Month'])
affordability_income_map_median = affordability_income_map_median.stack()
affordability_income_map_median = affordability_income_map_median.groupby('State').median()
affordability_income_map_median = affordability_income_map_median.reset_index()
affordability_income_map_median['State'] = affordability_income_map_median['State'].str.strip()
affordability_income_map_median['State'] = affordability_income_map_median['State'].astype(str)
affordability_income_map_median = affordability_income_map_median.merge(state_geo_xref,left_on='State',right_on='State')

years = ['2010','2011','2012','2013','2014','2015','2016','2017','2018']

#render the map
map_name = 'Median Income ($)'
m = folium.Map([43, -100], zoom_start=4)

for year in years:
    yr = folium.Choropleth(
    geo_data=state_geo,
    data=affordability_income_map_median,
    columns=['State', year],
    key_on='feature.id',
    fill_color='YlGn',
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name= f'{year} - {map_name}',
    overlay=True,
    name = f'{year}'
    )
    m.add_child(yr)

m.add_child(folium.LayerControl())

m.save(f"maps\{map_name}.html")

m
Out[113]:

MAP - Median Rent ($) w/ Overlay

In [114]:
#hideme

years = [col for col in gdf_income_tax.columns if col not in ['geometry','name','centroid_lon','centroid_lat','State','StateName']]

#render the map
map_name = 'Median Rent ($)'

m = folium.Map([43, -100], zoom_start=4)

for year in years:
    yr = folium.Choropleth(
    geo_data=state_geo,
    data=gdf_income_tax,
    columns=['State', year],
    key_on='feature.id',
    fill_color='YlGn',
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name= f'{year} - {map_name}',
    #overlay=True,
    name = year
    )
    m.add_child(yr)
    # add markers with basic information
    fg = folium.FeatureGroup(name=f'{year}-Rent')
    for lat, lon, val, name in zip(gdf_income_tax['centroid_lat'].tolist(), gdf_income_tax['centroid_lon'].tolist(), gdf_income_tax[year].tolist(), gdf_income_tax['name'].tolist()):
        html = f"""
        <h2>{name}<\h2><br>
        <h4>Median Rent ({year}): {int(round(val,0))} HUF <\h4>
        """
        fg.add_child(folium.Marker(location=[lat, lon], popup=html))
    m.add_child(fg)

folium.LayerControl().add_to(m)

m.save(f"maps\{map_name}.html")

m
Out[114]: